<?php
//消费券及领取操作

namespace App\Repositories;

use App\Models\DateModel;
use App\Models\DealModel;
use App\Models\KlineModel;
use App\Models\MoneyLogModel;
use App\Models\OperateModel;
use App\Models\StrategyModel;
use http\Env\Request;
use Illuminate\Support\Arr;


class OperateRepository extends BaseRepository
{
    public function __construct()
    {

    }

    private function formatTotalEarning ($earning)
    {
        $totalEarning = [];
        foreach ($earning as $key => $value) {
            $totalEarning[1] = null;
            if ($value == 'undefined' || $value == 'null') {
                $totalEarning[$key] = null;
            } else {
                $totalEarning[$key] = $value;
            }
        }
        return $totalEarning;
    }
    /**
     * 配置搜索条件
     * @param $params
     * @return array[]
     */
    public function getFilter ($params) {
        $user = request()->user();
        $where = [];
        $whereBetween = [];
        $where[] = [
            'admin_id', $user['id']
        ];
        if (!empty($params['strategy_id'])) {
            $where[] = [
                'strategy_id', $params['strategy_id']
            ];
        }
        if (!empty($params['is_finished'])) {
            $where[] = [
                'is_finished', $params['is_finished']
            ];
        }
        if (!empty($params['stock_code'])) {
            $where[] = [
                'stock_code', 'like', '%' . $params['stock_code'] . '%'
            ];
        }
        if (!empty($params['stock_name'])) {
            $where[] = [
                'stock_name', 'like', '%' . $params['stock_name'] . '%'
            ];
        }
        if (!empty($params['end_at'])) {
            $whereBetween[] = [
                'end_at', $params['end_at']
            ];
        }
        if (!empty($params['total_earning'])) {
            $totalEarning = $params['total_earning'];
            if ( count($totalEarning) > 0 ) {
                $totalEarning = $this->formatTotalEarning($totalEarning);
                if ($totalEarning[0] !== null && $totalEarning[1] === null) {
                    $where[] = [
                        'total_rate', '>=', $totalEarning[0] * 100
                    ];
                } else if ($totalEarning[0] === null && $totalEarning[1] !== null) {
                    $where[] = [
                        'total_rate', '<=', $totalEarning[1] * 100
                    ];
                } else {
                    $totalEarning = [
                        $totalEarning[0] * 100,
                        $totalEarning[1] * 100
                    ];
                    $whereBetween[] = [
                        'total_rate', $totalEarning
                    ];
                }
            }
        }
        $filter = [
            'where' => $where,
            'whereBetween' => $whereBetween
        ];
        return $filter;
    }

    /**
     * 获取列表
     * @param $params
     * @return array
     */
    public function getList ($params, $with = false)
    {
        if ($with) {
            $operateModel = OperateModel::with($with);
        } else {
            $operateModel = OperateModel::query();
        }
        $filter = $this->getFilter($params);
        $orders = $this->_getOrders($params);
        $lists = $this->getListItems($operateModel, $filter, $params['page_size'] ?? 20, ['*'], 'page', $orders);
        /*$statisticsModel = $this->getStatisticsModel($operateModel, $filter);
        $sumStockEarnings = $statisticsModel->sum('stock_earnings');
        dd($sumStockEarnings);*/
        $statistics = $this->getStatistics($operateModel, $filter);
        $lists['statistics'] = $statistics;
        return $this->_formatList($lists);
    }

    private function getStatistics ($operateModel, $filter) {
        $statistics = [];
        $statisticsModel = $this->getStatisticsModel($operateModel, $filter);
        $red = 'up';
        $green = 'down';
        $totalRate = $statisticsModel->sum('total_rate') / 100 ;
        $actualEarnings = $statisticsModel->sum('actual_earnings') / 100;
        $serviceCharge = $statisticsModel->sum('service_charge') / 100;
        $stockEarnings = $statisticsModel->sum('stock_earnings') / 100;
        $statistics['total_rate'] = [
            'label' => '整体涨幅',
            'value' => $totalRate . '%',
            'class' => $totalRate > 0 ? $red : $green
        ];

        $statistics['actual_earnings'] = [
            'label' => '实际盈利',
            'value' => $actualEarnings,
            'unit' => '元',
            'class' => $actualEarnings > 0 ? $red : $green
        ];
        $statistics['service_charge'] = [
            'label' => '手续费',
            'value' => $serviceCharge,
            'unit' => '元',
            'class' => $green
        ];
        $statistics['stock_earnings'] = [
            'label' => '股票盈利',
            'value' => $stockEarnings,
            'unit' => '元',
            'class' => $stockEarnings > 0 ? $red : $green
        ];
//        $statistics['stock_rate'] = $statisticsModel->sum('stock_rate') / 100;
        /*$statistics['real_rate'] = [
            'label' => '实际涨幅',
            'value' => $statisticsModel->sum('real_rate') / 100
        ];*/

        return $statistics;
    }

    /**
     * @param $lists
     * @return mixed
     */
    private function _formatList ($lists)
    {
        $user = request()->user();
        $strategies = StrategyModel::where('admin_id', $user['id'])
            ->pluck('name', 'id');
        foreach ($lists['list'] as $key => &$list) {
            $list['strategy_string'] = $strategies[$list['strategy_id']];
            $list['finished_string'] = OperateModel::FINISHEDS[$list['is_finished']];
        }
        return $lists;
    }

    /**
     * @param $params
     * @return mixed
     */
    public function save ($params)
    {
        $params = $this->formatSaveParams($params);
        return $this->saveOrUpdate('OperateModel', $params);
    }

    /**
     * @param $params
     * @return mixed
     */
    private function formatSaveParams ($params)
    {
        if (empty($params['be_monitor'])) {
            $params['be_monitor'] = false;
        }
        return $params;
    }

    /**
     * @param $id
     * @return false
     */
    public function getOperateInfo ($id)
    {
        $user = request()->user();
        if (empty($id)) {
            return false;
        }
        $operate = OperateModel::with(['note:id,operate_id,note'])
            ->where('id', $id)
            ->where('admin_id', $user['id'])
            ->select('id', 'strategy_id', 'code_prefix', 'stock_rate', 'buy_money', 'real_rate', 'stock_code', 'stock_name', 'stock_earnings', 'actual_earnings', 'service_charge', 'strategy_id', 'be_monitor', 'monitor_id', 'begin_at', 'end_at', 'duration', 'real_duration', 'is_finished', 'summary')
            ->first();
        if ($operate['strategy']) {
            $operate['strategy_name'] = $operate['strategy']['name'];
            $operate['finished_string'] = OperateModel::FINISHEDS[$operate['is_finished']];
            unset($operate['strategy']);
        }
        return $operate;
    }

    public function getOperateKline ()
    {
        // 格式为：
        // 1、date（日期） = 0
        // 2、yclose（前收盘价） = 1
        // 3、open（开盘价） = 2
        // 4、high（最高） = 3
        // 5、low （最低）= 4
        // 6、close（收盘价） = 5
        // 7、vol（成交量） = 6
        // 8、mount（成交金额） = 7
        // 计算公式：
        // 2、前收盘价 = 日期前的总收益
        // 3、开盘价 = 0
        // 4、最高价 = 持股中最高涨幅
        // 5、最低价 = 持股中最低跌幅
        // 6、收盘价 = 卖出时的涨跌幅
        // 7、成交量 = 卖出时的手数
        // 8、成交金额 = 卖出时的金额
        /*$list = '[
        [20240429, 0, 0, 10.01, 1.78, 7.94, 200, 8682],
        [20240430, 0, 7.94, 14.41, 7.16, 13.32, 1700, 11033],
        [20240508, 0, 13.32, 21.19, 11.06, 18.57, 700, 9527]
        ]';*/
        $list = '[
            [19991110, 29.5, 29.5, 29.8, 27.0, 27.75, 174085000, 4859102208.0], 
            [19991111, 27.75, 27.58, 28.38, 27.53, 27.71, 29403400, 821582208.0], 
            [19991112, 27.71, 27.86, 28.3, 27.77, 28.05, 15007900, 421591616.0]
        ]';
//        $list = '[["2024-04-29",0,0,100124,170.58,7.94,20000,803000],["2024-04-30",0,7.94,99820,-73.23,13.32,170000,1045500],["2024-05-08",0,13.32,43437,-242.32,18.57,70000,903700],["2024-05-14",0,18.57,35200,-98.58,18.98,30000,1005900]]';
        $list = [];
        $klines = KlineModel::where('kline_type_id', 1)
            ->select('low', 'high', 'date', 'open', 'close', 'vol', 'amount', 'yclose')
//            ->offset(0)->limit(2)
            ->get();
        foreach ($klines as $kline) {
            $listData = [];
            $listData[] = (int)str_replace('-', '', substr($kline['date'], 0, 10));
            $listData[] = null;                 // 涨幅根据此字段计算出来，需要展示涨幅是，打开备注
            $listData[] = $kline['open'];
            $listData[] = $kline['high'] / 100;
            $listData[] = $kline['low'];
            $listData[] = $kline['close'];
            $listData[] = $kline['vol'];
            $listData[] = $kline['amount'];
            $list[] = $listData;
        }
//        dd(json_encode($list));
        $data = '{"data": ' . json_encode($list) .  ' , "symbol": "0000-a.sh", "name": "金额指数", "code": 0 }';
//        $data = '{"data": ' . $list.  ' , "symbol": "0000-a.sh", "name": "金额指数", "code": 0 }';

        return $data;
    }

    /**
     * @param $operateId
     * @param $actualEarnings
     * @return array
     */
    public function finish ($operateId, $actualEarnings)
    {
        $user = request()->user();
        $operate = OperateModel::where('id', $operateId)
            ->where('admin_id', $user['id'])
            ->first();
        $monitorId = $operate['monitor_id'];
        if (empty($operate)) {
            return resultError('没有该操作实例');
        }
        $allDeals = $this->_getAllDeal($operateId, $user['id']);
        $dealMoney = $this->_getDealMoney($allDeals);
        if (!$dealMoney) {
            return resultError('买入和买出的数量不一致');
        }
        $stockEarnings = $dealMoney['saleMoney'] - $dealMoney['buyMoney'];
        if ($stockEarnings === false) {
            return resultError('买入和卖出的总手数不一致');
        } else {
            if ($actualEarnings > $stockEarnings) {
                return resultError('实际收入不能大于股票总收入，因为手续费 = 股票总收入 - 实际收入');
            }
            $totalMoney = $this->getTotalMoney($operateId, $user['id']) ? $this->getTotalMoney($operateId, $user['id']) : 1;
            $buyMoney = $dealMoney['buyMoney'] ? $dealMoney['buyMoney'] : 1;
            $totalRate = number_format($actualEarnings / $totalMoney * 100 , 2);
            $stockRate = number_format(($stockEarnings / $buyMoney) * 100, 2);
            $realRate = number_format(($actualEarnings / $buyMoney) * 100, 2);
            $beginAtEndAt = $this->_getBeginEndAt($allDeals);
            $beginAt = $beginAtEndAt['beginAt'];
            $endAt = $beginAtEndAt['endAt'];
            $operate->stock_earnings = $stockEarnings;
            $operate->actual_earnings = $actualEarnings;
            $operate->service_charge = $stockEarnings - $actualEarnings;
            $operate->is_finished = OperateModel::IS_FINISHED;
            $operate->buy_money = $dealMoney['buyMoney'];
            $operate->stock_rate = $stockRate;
            $operate->real_rate = $realRate;
            $operate->total_rate = $totalRate;
            $operate->begin_at = $beginAt;
            $operate->end_at = $endAt;
            $operate->admin_id = $user['id'];
            $operate->monitor_id = null;
            $operate->be_monitor = OperateModel::UNFINISHED;
            $operate->duration = $beginAtEndAt['duration'];
            $operate->real_duration = $this->getRealDuration($beginAt, $endAt);
            $rst = $operate->save();
            if ($rst !== false) {
                // 删除监听
                $monitorRepository = new MonitorRepository();
                $deleteRst = $monitorRepository->delete($monitorId);
                // 写入资金列表
                $moneyLogRepository = new MoneyLogRepository();
                $addMoneyLogRst = $moneyLogRepository->addByOperateFinished($actualEarnings, $user['id'], $operateId);
                if ($deleteRst) {
                    if ($addMoneyLogRst) {
                        return resultSuccess();
                    } else {
                        DB::rollBack();
                        return resultError('添加资金失败，请稍后再试');
                    }
                } else {
                    DB::rollBack();
                    return resultError('删除监听失败');
                }
            } else {
                DB::rollBack();
                return resultError('保存失败，请稍后再试');
            }
        }
    }

    public function getTotalMoney ($operateId, $userId)
    {
        $operateCreatedAt = DealModel::where('operate_id', $operateId)
            ->where('admin_id', $userId)
            ->orderBy('id', 'desc')
            ->value('created_at');
        $operateCreatedAt = $operateCreatedAt->format('Y-m-d H:i:s');
        $totalMoney = MoneyLogModel::where('admin_id', $userId)
            ->where('created_at', '<=', $operateCreatedAt)
            ->orderBy('id', 'desc')
            ->value('total');
        return $totalMoney;
    }

    /**
     * @param $beginAt
     * @param $endAt
     * @return mixed
     */
    public function getRealDuration ($beginAt, $endAt)
    {
        return DateModel::where('date', '>', substr($beginAt, 0, 10))
            ->where('date', '<=', substr($endAt, 0, 10))
            ->where('is_open', DateModel::IS_OPEN)
            ->count();
    }

    /**
     * @param $deals
     * @return array
     */
    private function _getBeginEndAt ($deals)
    {
        $beginAt = '';
        $endAt = '';
        foreach ($deals as $key => $deal) {
            if ($deal['deal_type'] == DealModel::DEAL_BUY) {
                if (empty($beginAt)) {
                    $beginAt = $deal['traded_at'];
                } else if (strtotime($deal['traded_at']) < strtotime($beginAt)) {
                    $beginAt = $deal['traded_at'];
                }
            } else if ($deal['deal_type'] == DealModel::DEAL_SALE) {
                if (empty($beginAt)) {
                    $endAt = $deal['traded_at'];
                } else if (strtotime($deal['traded_at']) > strtotime($endAt)) {
                    $endAt = $deal['traded_at'];
                }
            }
        }
        $difffTime = abs(strtotime($endAt) - strtotime($beginAt));
        $duration = floor($difffTime / (60 * 60 * 24));
        return [
            'beginAt' => $beginAt,
            'endAt' => $endAt,
            'duration' => $duration < 1 ? 1 : $duration
        ];
    }

    /**
     * @param $deals
     * @return false|float[]|int[]
     */
    private function _getDealMoney ($deals)
    {
        $buyMoney = 0;          // 买入价格
        $saleMoney = 0;         // 卖出价格
        $buyShareNumber = 0;    // 买入的总手数
        $saleShareNumber = 0;   // 卖出的总手数
        foreach ($deals as $key => $deal) {
            if ($deal['deal_type'] === DealModel::DEAL_BUY) {
                $buyMoney += $deal['deal_money'];
                $buyShareNumber += $deal['share_number'];
            } else if ($deal['deal_type'] === DealModel::DEAL_SALE) {
                $saleMoney += $deal['deal_money'];
                $saleShareNumber += $deal['share_number'];
            }
        }
        if ($saleShareNumber !== $buyShareNumber) {
            return false;
        } else {
            return [
                'saleMoney' => $saleMoney / 100,
                'buyMoney' => $buyMoney / 100
            ];
        }
    }

    /**
     * @param $operateId
     * @return mixed
     */
    private function _getAllDeal ($operateId, $adminId)
    {
        return DealModel::where('admin_id', $adminId)
            ->where('operate_id', $operateId)
            ->select('price', 'deal_type', 'share_number', 'traded_at', 'deal_money')
            ->get();
    }

    /**
     * @param null $strategyId
     * @return mixed
     */
    public function getSuccessCount ($strategyId = null)
    {
        $user = request()->user();
        return OperateModel::where('admin_id', $user['id'])
            ->when($strategyId, function ($query) use ($strategyId) {
                $query->where('strategy_id', $strategyId);
            })
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->where('actual_earnings', '>=', 0)
            ->count();
    }

    /**
     * @param null $strategyId
     * @return mixed
     */
    public function getFailCount ($strategyId = null)
    {
        $user = request()->user();
         return OperateModel::where('admin_id', $user['id'])
            ->when($strategyId, function ($query) use ($strategyId) {
                $query->where('strategy_id', $strategyId);
            })
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->where('actual_earnings', '<', 0)
            ->count();
    }

    /**
     * @param null $strategyId
     * @return mixed
     */
    public function getDealCount ($strategyId = null)
    {
        $user = request()->user();
        return OperateModel::where('admin_id', $user['id'])
            ->when($strategyId, function ($query) use ($strategyId) {
                $query->where('strategy_id', $strategyId);
            })
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->count();
    }

    /**
     * 获取某月收益
     * @param $month
     * @return mixed
     */
    public function getRateByMonth ($month)
    {
        $user = request()->user();
        return OperateModel::where('admin_id', $user['id'])
            ->whereMonth('end_at', $month)
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->sum('total_rate');
    }

    /**
     * 获取总收益
     * @return mixed
     */
    public function getAllRate ()
    {
        $user = request()->user();
        return OperateModel::where('admin_id', $user['id'])
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->sum('total_rate');
    }

    /**
     * @param $followId
     * @return bool
     */
    public function updateBeMonitor ($operateId, $monitorId)
    {
        $follow = OperateModel::find($operateId);
        $follow->be_monitor = 1;
        $follow->monitor_id = $monitorId;
        $follow->save();
    }

    private function _getDealPrice ($deals)
    {
        $unFinishedPrices = [];
        $unFinishedNumber = [];
        $finishedPrices = [];
        $finishedNumber = [];
        $isSale = false;
        foreach ($deals as $deal) {
            if ($deal['deal_type'] == DealModel::DEAL_SALE) {
                $isSale = true;
                if (empty($unFinishedPrices[$deal['stock_code']])) {
                    $unFinishedPrices[$deal['stock_code']] = ($deal['price'] * $deal['share_number']);
                    $unFinishedNumber[$deal['stock_code']] = $deal['share_number'];
                } else {
                    $unFinishedPrices[$deal['stock_code']] += ($deal['price'] * $deal['share_number']);
                    $unFinishedNumber[$deal['stock_code']] += $deal['share_number'];
                }
            } else if ($deal['deal_type'] == DealModel::DEAL_BUY) {
                if (empty($finishedPrices[$deal['stock_code']])) {
                    $finishedPrices[$deal['stock_code']] = ($deal['price'] * $deal['share_number']);
                    $finishedNumber[$deal['stock_code']] = $deal['share_number'];
                } else {
                    $finishedPrices[$deal['stock_code']] += ($deal['price'] * $deal['share_number']);
                    $finishedNumber[$deal['stock_code']] += $deal['share_number'];
                }
            }
        }
        if (!$isSale) {
            $unFinishedPrices = $finishedPrices;
            $unFinishedNumber = $finishedNumber;
        }
        return [
            'unFinishedPrices' => [
                'prices' => $unFinishedPrices,
                'numbers' => $unFinishedNumber
            ],
            'finishedPrices' => [
                'prices' => $finishedPrices,
                'numbers' => $finishedNumber
            ]
        ];
    }

    public function addUnFinishDealData ($list)
    {
        $followRepository = new FollowRepository();
        $unFinishedOperateData = $this->getUnFinishedOperateData($list);
        $unFinishedOperateIds = $unFinishedOperateData['operate_ids'];
        $deals = DealModel::whereIn('operate_id', $unFinishedOperateIds)
            ->get();
        $url = $followRepository->_getApiUrlByList($deals, '');
        $stockTextInfo = $followRepository->_getStockTextInfosByUrl($url);
        $stockInfos = $followRepository->_getStockInfo($stockTextInfo);
        $stockInfos = Arr::pluck($stockInfos, 'current_price', 'stock_code');
//        $dealPrices = $this->_getDealPrice($deals);
        $earnings = $this->_getEarning($deals, $stockInfos);
        $list = $this->_setUnFinishedDealData($list, $earnings);
        return $list;
    }

    public function getListDealCount ($dealIds) {
        $dealCount = DealModel::whereIn('operate_id', $dealIds)
            ->selectRaw('operate_id, count(*) as count')
            ->groupBy('operate_id')
            ->pluck('count', 'operate_id');
        return $dealCount;
    }

    private function _setUnFinishedDealData ($list, $earnings)
    {
        $listIds = [];
        if (!empty($earnings)) {
            foreach ($list as &$value) {
                if (empty($value['stock_earnings'])) {
                    if (!empty($earnings[$value['stock_code']])) {
                        $value['stock_earnings'] = $earnings[$value['stock_code']]['earning'] / 100;
                        $value['stock_rate'] = $earnings[$value['stock_code']]['rate'];
                        $value['buy_money'] = $earnings[$value['stock_code']]['buy_money'] / 100;
                    }
                }
                $listIds[] = $value['id'];
            }
        }
        return [
            'list' => $list,
            'list_ids' => $listIds
        ];
    }

    private function _getEarning ($deals, $stockInfos)
    {
        $priceData = [];
        foreach ($stockInfos as $code => $currentPrice) {
            $priceData[$code] = [
                'buy_money' => 0,
                'sale_money' => 0,
                'buy_number' => 0,
                'sale_number' => 0,
                'unfinish_number' => 0,
                'unfinish_money' => 0,
            ];
            foreach ($deals as $deal) {
                if ($code == $deal['stock_code']) {
                    if ($deal['deal_type'] == DealModel::DEAL_SALE) {
                        // 计算出售的金额和数量
                        $priceData[$code]['sale_money'] += $deal['deal_money'];
                        $priceData[$code]['sale_number'] += $deal['share_number'];
                    } else if ($deal['deal_type'] == DealModel::DEAL_BUY) {
                        // 计算购买金额和数量
                        $priceData[$code]['buy_money'] += $deal['deal_money'];
                        $priceData[$code]['buy_number'] += $deal['share_number'];
                    }
                }
            }
            // 计算没出售的数量和金额
            $priceData[$code]['unfinish_number'] = $priceData[$code]['buy_number'] - $priceData[$code]['sale_number'];
            $priceData[$code]['unfinish_money'] = $priceData[$code]['unfinish_number']  * $currentPrice * 100;
            // 计算盈利金额和比例
            $priceData[$code]['earning'] = $priceData[$code]['sale_money'] + $priceData[$code]['unfinish_money'] - $priceData[$code]['buy_money'];
            $buy = $priceData[$code]['buy_money'] ? $priceData[$code]['buy_money'] : 1;
            $priceData[$code]['rate'] = $priceData[$code]['earning'] / $buy * 100;
        }
        return $priceData;

        /*$unFinishedPrices = $dealPrices['unFinishedPrices'];
        $earnings = [];
        $rate = [];
//        dd($stockInfos);
        foreach ($stockInfos as $code => $price) {
            $earnings[$code] = ($price * $unFinishedPrices['numbers'][$code] - $unFinishedPrices['prices'][$code]);
            $rate[$code] = $earnings[$code] / $unFinishedPrices['prices'][$code] * 100;
        }
        return [
            'price' => $earnings,
            'rate' => $rate,
        ];*/
    }

    private function getUnFinishedOperateData ($list)
    {
        $unFinishedOperateIds = [];
        $stocks = [];
        foreach($list as $key => $item) {
            if ($item['is_finished'] == OperateModel::UNFINISHED) {
                $unFinishedOperateIds[] = $item['id'];
                $stocks[] = $item['code_prefix'] . $item['stock_code'];
            }
        }
        return [
            'operate_ids' => $unFinishedOperateIds,
            'stocks' => $stocks
        ];
    }
}
